/*
 * Description: 通信客户端代理基类，实现了客户端的基础通信功能。
 */
#include <iostream>
#include <vector>
#include <atomic>
#include <csignal>
#include "log.h"
#include "link_base.h"
#include "iservice.h"

namespace COMMON {
IService::IService(const std::string &server, const std::string &client)
    : m_server(server), m_client(client)
{
}

IService::~IService()
{
    LOG_DEBUG("IService::~IService");
    Destroy();
    LOG_DEBUG("IService::~IService finished");
}

void IService::Destroy()
{
    LOG_DEBUG("IService::Destroy");
    m_connected = false;
    m_callRecordsCache.WakeUpAll();
    LOG_DEBUG("IService::Destroy finished");
    m_msgSock = nullptr;
    LOG_DEBUG("IService::Destroy finished");
}

int IService::ReconnectServer()
{
    LOG_WARN(FORMAT(s) "'s proxy hasn't implemented reconnect method, here base IService just return fail",
        m_server.c_str());
    return RECONNECT_SERVER_ERROR;
}

int IService::InternalReconnectServer()
{
    LOG_DEBUG("IService::InternalReconnectServer");
    std::unique_lock<std::mutex> lock(m_mutex);
    if (m_connected) {
        return OK;
    }
    LOG_DEBUG(FORMAT(s) " destroys all connections with server: " FORMAT(s), m_client.c_str(), m_server.c_str());
    Destroy();
    if (ReconnectServer() != OK) {
        LOG_WARN("client: " FORMAT(s) " reconnects server: " FORMAT(s) " fail, server may exit.",
            m_client.c_str(), m_server.c_str());
        return DEFAULT_ERROR;
    }
    LOG_INFO("client: " FORMAT(s) " reconnects server: " FORMAT(s) " succeed", m_client.c_str(), m_server.c_str());
    return OK;
}

void IService::OnDisconnected()
{
}

void IService::SetReadTimeout(int timeout)
{
    m_curTimeout = timeout;
}

void IService::InternalOnDisconnected()
{
    m_connected = false;
    OnDisconnected();
}

bool IService::IsConnected() const
{
    return m_connected;
}

int IService::CloseEventChannel()
{
    m_connected = false;
    if (m_msgSock != nullptr) {
        return m_msgSock->Close();
    }
    return 0;
}

// 解析server端发过来的数据
void IService::HandleResponse(const std::shared_ptr<MsgSock> &msgSock, void *arg)
{
    LOG_DEBUG("IService::HandleResponse");
    if (msgSock == nullptr) {
        LOG_ERR("msg sock is nullptr when iserver disconnect");
        return;
    }
    if (arg == nullptr) {
        LOG_ERR("arg is nullptr when iserver disconnect");
        return;
    }
    auto callRecordsCache = static_cast<CallRecordsCache*>(arg);
    std::vector<char> messageBuffer;
    MessageType messageType = MessageType::INIT_TYPE;
    // 读取数据
    if (LinkBase::ReceiveMessage(msgSock, messageType, messageBuffer) != OK) {
        LOG_ERR("iservice handle response, recv msg failed");
        return;
    }

    // 解析返回值
    Guest::protobuf::link::Result result;
    result.ParseFromArray(messageBuffer.data(), static_cast<int>(messageBuffer.size()));

    // 把返回值存入callRecordsCache中的callrecord
    callRecordsCache->SetResult(result);

    // 唤醒callRecordsCache中的callrecord
    callRecordsCache->WakeUp(result.callid());
}

// client发数据给服务端
int IService::CallMethodSync(int id, const google::protobuf::MessageLite &parameters,
    google::protobuf::MessageLite *result, bool asyncInService)
{
    LOG_DEBUG(FORMAT(s) " call sync method id " FORMAT(d) " to " FORMAT(s), m_client.c_str(), id, m_server.c_str());
    if (result == nullptr) {
        LOG_ERR("result is nullptr");
        return INVALID_PROTO_RESULT;
    }
    const size_t messageSize = parameters.ByteSizeLong();
    /* 限制发送大小 0-256M(256 * 1024 * 1024)字节 */
    if (messageSize > MESSAGE_MAX_SIZE) {
        LOG_ERR("parameters size exceeds limit of sync call, message pack failed, size:" FORMAT(lu), messageSize);
        return INVALID_MESSAGE_SIZE;
    }

    if (!m_connected) {
        LOG_WARN(FORMAT(s) " has lost connection with " FORMAT(s) " during calling its sync method",
            m_client.c_str(), m_server.c_str());
            return RECONNECT_SERVER_ERROR;
    }

    // 打包调用
    uint32_t callID = GetNextID();
    auto const &call = PackCall(callID, id, parameters, asyncInService);

    // 储存调用
    m_callRecordsCache.AddCallRecord(callID, result);

    // 发送消息
    int ret = SendMessage(m_msgSock, MessageType::SYNC_CALL, call);
    if (ret != OK) { // 发送消息失败了，删除保存记录
        m_callRecordsCache.RemoveCallRecord(callID);
        return ret;
    }

    // 等待返回值
    return m_callRecordsCache.WaitForResponse(callID);
}


// 异步发数据给服务端
int IService::CallMethodAsync(int id, const google::protobuf::MessageLite &parameters, bool asyncInService)
{
    LOG_DEBUG("IService::CallMethodAsync");
    const size_t messageSize = parameters.ByteSizeLong();
    /* 限制发送大小 0-256M(256 * 1024 * 1024)字节 */
    if (messageSize > MESSAGE_MAX_SIZE) {
        LOG_ERR("parameters size exceeds limit of async call, message pack failed, size:" FORMAT(lu), messageSize);
        return INVALID_MESSAGE_SIZE;
    }

    if (!m_connected) {
        LOG_WARN(FORMAT(s) " has lost connection with " FORMAT(s) " during calling its Async method",
                 m_client.c_str(), m_server.c_str());
        return RECONNECT_SERVER_ERROR;
    }

    auto const &call = PackCall(ASYNC_CALLID, id, parameters, asyncInService);
    return SendMessage(m_msgSock, MessageType::NOTIFICATION, call);
}

uint32_t IService::GetNextID() const
{
    static std::atomic<uint32_t> id(1);
    return id++;
}

// 打包需要发送的数据
Guest::protobuf::link::Call IService::PackCall(uint32_t callID, int methodID,
    const google::protobuf::MessageLite &parameters, bool asyncInService) const
{
    LOG_DEBUG("IService::PackCall");
    std::vector<unsigned char> buffer(parameters.ByteSizeLong(), OK);

    parameters.SerializeWithCachedSizesToArray(buffer.data());
    Guest::protobuf::link::Call call;
    call.set_callid(callID);
    call.set_methodid(methodID);
    call.set_parameters(buffer.data(), buffer.size());
    call.set_async(asyncInService);

    return call;
}

int IService::SendMessage(const std::shared_ptr<MsgSock> &msgSock, MessageType type,
    const google::protobuf::MessageLite &message) const
{
    LOG_DEBUG("IService::SendMessage");
    return LinkBase::SendMessage(msgSock, type, message);
}

// 初始化client端事件通道
int IService::EstablishEventChannel(int port,
    const std::string& userId, const std::string& appId, const std::string& vmName)
{
    LOG_INFO("IService::EstablishEventChannel");
    if (m_msgSock != nullptr) {
        LOG_ERR("EventChannel's sock has inited");
        return SOCKET_SERVER_INITED;
    }
    std::shared_ptr<MsgSock> msgSock {};
    SocketInfo info { userId, appId, vmName, port };
    int ret = CreateChannel(info, msgSock);
    if (ret != OK) {
        LOG_ERR("create event channel failed");
        return CREATE_CHANNLE_FAILED;
    }
    LOG_INFO("IService::EstablishEventChannel before handshake");
    Guest::protobuf::link::Handshake handshake;
    handshake.set_connectionmode(GUEST_EVENT);
    handshake.set_clientname(m_client);
    if (LinkBase::SendMessage(msgSock, MessageType::INIT_TYPE, handshake) != OK) {
        LOG_ERR("iservice send authstring handshake failed!");
        return MSGSOCK_SEND_FAILED;
    }
    LOG_INFO("IService::EstablishEventChannel sent handshake");
    m_msgSock = std::move(msgSock);
    LOG_INFO("IService::EstablishEventChannel after move");
    LOG_INFO("IService::EstablishEventChannel end");
    m_connected = true;
    return OK;
}

// 创建连接通道
int IService::CreateChannel(SocketInfo info, std::shared_ptr<MsgSock> &msgSock)
{
    LOG_INFO("IService::CreateChannel");

    try {
        msgSock = std::make_unique<MsgSock>();
    } catch (const std::exception &e) {
        LOG_ERR("make unique error!");
        return INIT_MSGSOCK_FAILED;
    }
    if (msgSock->Create(info) != OK) {
        LOG_ERR("create sock failed, errno:" FORMAT(d), errno);
        return MSGSOCK_CREATE_FAILED;
    }

    if (m_curTimeout != DEFAULT_TIME_OUT) {
        msgSock->SetRecvTimeout(m_curTimeout);
        m_curTimeout = DEFAULT_TIME_OUT;
    }
    LOG_INFO("IService::CreateChannel end");
    return OK;
}
}
